#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define BUFSIZE 10   //  缓冲区大小
#define CON_NUM 1    //  消费者数量
#define PRODUCTS 50 // 生产总数
#define INVAILD_NUMBER  -1 //定义无效数字
const int key = 1190202209;
const char *full_name = "lab6full";
const char *empty_name = "lab6empty";
const char *mutex_name = "lab6mutex";
sem_t *empty, *full, *mutex;
int shmid;
void Producer()
{
    int fd, num = 0;
    int *buf,*p;
    buf = (int *)shmat(shmid,NULL,0);
    while (num <= PRODUCTS)
    {
        sem_wait(empty);
        sem_wait(mutex);
        p = buf;
        while(*p!= INVAILD_NUMBER && p< buf + BUFSIZE)
            p++;
        *p=num;
        sem_post(mutex);
        sem_post(full);
        num++;
    }
    return;
}

void Consumer()
{
    int fd;
    int *buf,*p,*q;
    buf = (int *)shmat(shmid,NULL,0);
    while (1)
    {
        sem_wait(full);
        sem_wait(mutex);
        printf("%d: %d\n",getpid(),*buf);
        fflush(stdout);
        q = buf;
        p = buf + 1;
        *q = INVAILD_NUMBER;//consume
        while(*p!=INVAILD_NUMBER && p< buf + BUFSIZE){
            *q = *p;
            *p = INVAILD_NUMBER;
            p++;q++;
        }
        sem_post(mutex);
        sem_post(empty);
    }
    return;
}

int main()
{
    int i;
    pid_t pid;
    //删除信号
    sem_unlink(full_name);
    sem_unlink(empty_name);
    sem_unlink(mutex_name);
    empty = sem_open(empty_name, O_CREAT, 0666, BUFSIZE); // 创建信号量empty，初始为最大值（剩余空间）
    full = sem_open(full_name, O_CREAT, 0666, 0);         // 创建信号量full，初始为0（已生产空间）
    mutex = sem_open(mutex_name, O_CREAT, 0666, 1);       // 创建互斥信号量，同时只有1个进程可以进行访问临界区
    // 创建共享内存
    shmid = shmget(key, BUFSIZE*sizeof(int), 0666|IPC_CREAT);
    memset(shmat(shmid,NULL,0),-1,BUFSIZE*sizeof(int));
    // 创建生产者
    if (!(pid = fork()))
    {
        Producer();
        return 0;
    }
    // 创建消费者
    for (i = 0; i < CON_NUM; i++)
    {
        if (!fork())
        {
            Consumer();
            return 0;
        }
    }
    // 回收生产完毕的生产者进程
    waitpid(pid, NULL, 0);
    usleep(500);         //稍微等一会
    kill(0, SIGKILL); //杀死所有消费者进程
    //删除信号
    sem_unlink(full_name);
    sem_unlink(empty_name);
    sem_unlink(mutex_name);
    return 0;
}